# main.py
import sys
import os
import logging
import traceback
import faulthandler
import datetime
import psutil
import matplotlib
import threading
import gc
import ctypes
import time

# 设置Matplotlib后端
try:
    matplotlib.use('QtAgg', force=True)
    logging.info("使用 QtAgg 后端")
except ImportError:
    try:
        matplotlib.use('Qt5Agg', force=True)
        logging.info("使用 Qt5Agg 后端")
    except ImportError:
        try:
            matplotlib.use('TkAgg', force=True)
            logging.info("使用 TkAgg 后端")
        except ImportError:
            matplotlib.use('Agg', force=True)
            logging.warning("使用 Agg 后端 (无交互功能)")

# 启用故障处理程序
faulthandler.enable()

# 配置日志
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(message)s',
                    handlers=[logging.StreamHandler(), logging.FileHandler('app_debug.log')])

# 设置UTF-8模式
os.environ['PYTHONUTF8'] = '1'

# 导入PyQt
from PyQt5.QtWidgets import QApplication, QMessageBox
from PyQt5.QtGui import QFont
from PyQt5.QtCore import QEvent
from modules.stock_analysis_app import StockAnalysisApp, QExceptionEvent  # 导入自定义事件类

# 内存监控函数
def memory_monitor():
    """监控内存使用情况"""
    while True:
        try:
            process = psutil.Process(os.getpid())
            mem_info = process.memory_info()
            logging.info(f"内存使用: RSS={mem_info.rss / 1024 / 1024:.2f}MB, VMS={mem_info.vms / 1024 / 1024:.2f}MB")
        except Exception as e:
            logging.error(f"获取内存信息失败: {str(e)}")
        time.sleep(30)  # 每30秒记录一次

def excepthook(exc_type, exc_value, exc_tb):
    """全局异常处理"""
    tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb))
    logging.error(f"未捕获的异常:\n{tb}")

    # 添加内存信息记录
    try:
        process = psutil.Process(os.getpid())
        mem_info = process.memory_info()
        logging.error(f"内存使用: RSS={mem_info.rss / 1024 / 1024:.2f}MB, VMS={mem_info.vms / 1024 / 1024:.2f}MB")
    except Exception as mem_e:
        logging.error(f"获取内存信息失败: {str(mem_e)}")

    # 记录到文件
    with open('crash_report.txt', 'w') as f:
        f.write(f"崩溃报告\n{'=' * 40}\n")
        f.write(f"时间: {datetime.datetime.now()}\n")
        f.write(f"异常类型: {exc_type.__name__}\n")
        f.write(f"异常信息: {str(exc_value)}\n")
        try:
            f.write(f"内存使用: RSS={mem_info.rss / 1024 / 1024:.2f}MB, VMS={mem_info.vms / 1024 / 1024:.2f}MB\n")
        except:
            f.write("内存信息: 无法获取\n")
        f.write(f"堆栈跟踪:\n{tb}")

    # 尝试显示错误消息
    try:
        QMessageBox.critical(
            None,
            "未捕获的异常",
            f"发生未处理的异常:\n\n{str(exc_value)}\n\n" +
            f"详细信息已记录到日志中"
        )
    except:
        pass

    # 调用原始异常处理
    sys.__excepthook__(exc_type, exc_value, exc_tb)

def thread_excepthook(args):
    """线程异常处理"""
    logger = logging.getLogger(__name__)
    logger.error(f"线程异常: {args.thread.name if args.thread else 'Unknown thread'}")
    logger.error(f"异常类型: {args.exc_type.__name__}")
    logger.error(f"异常信息: {args.exc_value}")

    # 获取堆栈跟踪
    try:
        tb_str = ''.join(traceback.format_tb(args.exc_traceback))
        logger.error(f"堆栈跟踪:\n{tb_str}")
    except:
        logger.error("无法获取堆栈跟踪")

    # 尝试显示错误消息
    try:
        app = QApplication.instance()
        if app:
            # 在主线程显示错误
            app.postEvent(app, QExceptionEvent(str(args.exc_value), tb_str))
    except:
        pass

if __name__ == "__main__":
    # 启动内存监控线程
    mem_thread = threading.Thread(target=memory_monitor, daemon=True)
    mem_thread.start()

    # 设置全局异常钩子
    sys.excepthook = excepthook
    threading.excepthook = thread_excepthook

    # 设置堆栈大小 (Windows)
    if sys.platform == "win32":
        try:
            # 设置线程堆栈大小为32MB
            size = ctypes.c_ulong(32 * 1024 * 1024)
            ctypes.windll.kernel32.SetThreadStackGuarantee(ctypes.byref(size))
            logging.info(f"已将线程堆栈大小设置为 {size.value} 字节")
        except Exception as e:
            logging.error(f"设置线程堆栈大小失败: {str(e)}")

    try:
        # 设置Qt插件路径
        if sys.platform == 'win32':
            # 尝试多种可能的路径
            possible_plugin_paths = [
                os.path.join(sys.prefix, 'Library', 'plugins'),
                os.path.join(sys.prefix, 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
                os.path.join(sys.prefix, 'Lib', 'site-packages', 'PyQt5', 'Qt', 'plugins'),
                os.path.join(os.path.dirname(sys.executable), 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins'),
                os.path.join(os.path.dirname(sys.executable), 'Lib', 'site-packages', 'PyQt5', 'plugins'),
                "C:/Qt/5.15.2/msvc2019_64/plugins",
                "C:/Qt/5.15.2/mingw81_64/plugins",
                os.path.join(os.path.expanduser("~"), "Anaconda3", "Library", "plugins"),
                os.path.join(os.path.dirname(sys.executable), "plugins")
            ]

            # 添加调试信息
            logging.info("Searching for Qt plugins in:")
            found = False
            for path in possible_plugin_paths:
                exists = os.path.exists(path)
                logging.info(f"  {path} {'[FOUND]' if exists else '[MISSING]'}")
                if exists:
                    os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = path
                    logging.info(f"Set QT_QPA_PLATFORM_PLUGIN_PATH to: {path}")
                    found = True
                    break

            if not found:
                logging.warning("未找到有效的Qt插件路径")
                # 尝试使用默认路径
                default_path = os.path.join(sys.prefix, 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins')
                os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = default_path
                logging.warning(f"使用默认路径: {default_path}")

        # 创建应用
        app = QApplication(sys.argv)
        app.setStyle("Fusion")

        # 自定义事件处理器
        def event_handler(event):
            if isinstance(event, QExceptionEvent):
                QMessageBox.critical(
                    None,
                    "线程异常",
                    f"线程发生未处理异常:\n\n{event.message}\n\n堆栈跟踪:\n{event.traceback}"
                )
                return True
            return False

        # 安装事件过滤器
        app.eventFilter = lambda obj, event: event_handler(
            event) if event.type() == QExceptionEvent.EVENT_TYPE else False
        app.installEventFilter(app)

        # 尝试不同的渲染后端
        render_backends = [
            ("Software", "AA_UseSoftwareOpenGL"),
            ("DesktopOpenGL", "AA_UseDesktopOpenGL"),
            ("OpenGLES", "AA_UseOpenGLES")
        ]

        backend_used = None

        for backend_name, backend_attr in render_backends:
            try:
                from PyQt5.QtCore import Qt
                from PyQt5.QtOpenGL import QGLFormat

                # 设置默认格式
                format = QGLFormat.defaultFormat()
                format.setSampleBuffers(True)
                format.setSamples(4)  # 4x 多重采样抗锯齿
                QGLFormat.setDefaultFormat(format)

                # 设置渲染后端
                backend = getattr(Qt, backend_attr)
                app.setAttribute(backend)
                logging.info(f"尝试使用 {backend_name} 渲染后端")

                # 创建主窗口
                window = StockAnalysisApp()
                window.show()

                # 运行应用
                backend_used = backend_name
                logging.info(f"成功使用 {backend_name} 渲染后端")
                break
            except Exception as e:
                logging.warning(f"无法使用 {backend_name} 渲染后端: {str(e)}")

        # 如果所有后端都失败，使用默认设置
        if backend_used is None:
            logging.warning("所有渲染后端尝试失败，使用默认设置")
            window = StockAnalysisApp()
            window.show()

        # 设置默认字体
        font = QFont("Arial", 10)
        app.setFont(font)

        # 运行应用
        logging.info("应用程序启动成功，进入主事件循环")
        sys.exit(app.exec_())
    except Exception as e:
        logging.error(f"应用程序启动失败: {str(e)}")
        # 尝试显示错误消息
        try:
            QMessageBox.critical(
                None,
                "Fatal Error",
                f"应用程序启动失败:\n\n{str(e)}\n\n" +
                "请检查日志获取更多详情。"
            )
        except:
            # 如果无法显示GUI错误，打印到控制台
            print(f"致命错误: {str(e)}")
        sys.exit(1)